package com.thememaker.lockstyle;

import com.alibaba.fastjson.JSONObject;
import com.thememake.entity.ThemeFileInfo;
import com.thememake.util.xml.XmlUtils;
import com.thememake.util.xml.fontcolor.ReadXmlUtils;
import com.thememaker.lockstyle.bean.Comment;
import com.thememaker.lockstyle.bean.Lockscreen;
import com.thememaker.lockstyle.bean.Var;
import com.thememaker.lockstyle.bean.command.*;
import com.thememaker.lockstyle.bean.ui.*;
import com.thememaker.util.AutoSaveUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Optional;
import java.util.function.Consumer;

/**
 * Created by jiajun.jiang on 2018/2/24.
 */
public class LockstylePaserUtils {
    private static String lockscreen = "lockscreen";
    private static String baseName;
    private static String groupName = "";

    public static Lockscreen paserLockScreenXml(Document document) {
        Lockscreen lockscreen = new Lockscreen();
        Element rootElement = document.getDocumentElement();
        try {
            lockscreen = (Lockscreen) XmlUtils.xml2java(Lockscreen.class, rootElement);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        NodeList nodeList = rootElement.getChildNodes();
        lockscreen.setLockScreenViewMap(paserView(nodeList));
        return lockscreen;
    }

    private static LinkedHashMap<String, BaseView> paserView(NodeList nodeList) {
        LinkedHashMap<String, BaseView> viewMap = new LinkedHashMap<String, BaseView>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            try {
                baseName = lockscreen + groupName;
                switch (node.getNodeName()) {
                    case "Var":
                        Var var = (Var) XmlUtils.xml2java(Var.class, node);
                        String viewName = baseName + "_Var_" + i;
                        var.setId(viewName);
                        viewMap.put(viewName, var);
                        break;
                    case "Image":
                        Image image = (Image) XmlUtils.xml2java(Image.class, node);
                        String imageName = baseName + "_Image_" + i;
                        image.setId(imageName);
                        viewMap.put(imageName, image);
                        break;
                    case "Button":
                        Button button = (Button) XmlUtils.xml2java(Button.class, node);
                        String buttonName = baseName + "_Button_" + i;
                        button.setId(buttonName);
                        parseTriggersCommand(((Element) node), buttonName, button);
                        viewMap.put(buttonName, button);
                        break;
                    case "Text":
                        Text text = (Text) XmlUtils.xml2java(Text.class, node);
                        String textName = baseName + "_Text_" + i;
                        text.setId(textName);
                        viewMap.put(textName, text);
                        break;
                    case "Wallpaper":
                        Wallpaper wallpaper = (Wallpaper) XmlUtils.xml2java(Wallpaper.class, node);
                        String wallpaperName = baseName + "_Wallpaper_" + i;
                        wallpaper.setId(wallpaperName);
                        viewMap.put(wallpaperName, wallpaper);
                        break;
                    case "Group":
                        Group group = (Group) XmlUtils.xml2java(Group.class, node);
                        groupName = groupName + "_Group_" + i;
                        baseName = lockscreen + groupName;
                        group.setGroupViewMap(paserView(node.getChildNodes()));
                        group.setId(baseName);
                        viewMap.put(baseName, group);
                        groupName = "";
                        break;
                    case "Unlocker":
                        Unlocker unlocker = (Unlocker) XmlUtils.xml2java(Unlocker.class, node);
                        String unlockerName = baseName + "_Unlocker_" + i;
                        unlocker.setId(unlockerName);
                        parseSliderCommand((Element) node, "StartPoint", unlockerName, unlocker);
                        viewMap.put(unlockerName, unlocker);
                        break;
                    case "Slider":
                        Slider slider = (Slider) XmlUtils.xml2java(Slider.class, node);
                        String sliderName = baseName + "_Slider_" + i;
                        slider.setId(sliderName);
                        parseSliderCommand((Element) node, "StartPoint", sliderName, slider);
                        viewMap.put(sliderName, slider);
                        break;
                    case "ExternalCommands":
                        ExternalCommands externalCommands = (ExternalCommands) XmlUtils.xml2java(ExternalCommands.class, node);
                        String externalName = baseName + "_ExternalCommands_" + i;
                        externalCommands.setId(externalName);
                        viewMap.put(externalName, externalCommands);
                        break;
                    case "#comment":
                        Comment comment = new Comment();
                        comment.setValue(node.getNodeValue());
                        String commentKey = baseName + "_comment_" + i;
                        comment.setId(commentKey);
                        viewMap.put(commentKey, comment);
                        break;
                    default:
                        break;
                }
            } catch (JAXBException e) {
                e.printStackTrace();
            }
        }
        return viewMap;
    }

    private static void parseSliderCommand(Element elementNode, String tagName, String parentKey, Slider slider) throws JAXBException {
        NodeList nodeList = elementNode.getElementsByTagName(tagName);
        if (nodeList != null && nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element element = (Element) nodeList.item(i);
                parseSliderCommand(element, "ReachedState", parentKey, slider);
                parseSliderCommand(element, "ReachedUpState", parentKey, slider);
                parseSliderCommand(element, "ReachedDownState", parentKey, slider);
                parseSliderCommand(element, "NormalState", parentKey, slider);
                parseSliderCommand(element, "PressedState", parentKey, slider);
                switch (tagName) {
                    case "ReachedState":
                        parseTriggerCommand(element, parentKey + "_" + tagName, o -> {
                            slider.getStartPoint().getReachedState().getTrigger().setCommandMap(o);
                        });
                        break;
                    case "ReachedUpState":
                        parseTriggerCommand(element, parentKey + "_" + tagName, o -> {
                            slider.getStartPoint().getReachedUpState().getTrigger().setCommandMap(o);
                        });
                        break;
                    case "ReachedDownState":
                        parseTriggerCommand(element, parentKey + "_" + tagName, o -> {
                            slider.getStartPoint().getReachedDownState().getTrigger().setCommandMap(o);
                        });
                        break;
                    case "NormalState":
                        parseTriggerCommand(element, parentKey + "_" + tagName, o -> {
                            slider.getStartPoint().getNormalState().getTrigger().setCommandMap(o);
                        });
                        break;
                    case "PressedState":
                        parseTriggerCommand(element, parentKey + "_" + tagName, o -> {
                            slider.getStartPoint().getPressedState().getTrigger().setCommandMap(o);
                        });
                        break;
                    default:
                        parseSliderCommand(element, tagName, parentKey, slider);
                        break;
                }

            }
        }
    }

    private static void parseTriggersCommand(Element elementNode, String parentKey, Button button) throws JAXBException {
        NodeList nodeList = elementNode.getElementsByTagName("Triggers");
        if (nodeList != null && nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element triggersElement = (Element) nodeList.item(i);
                parseTriggerListCommand(triggersElement, parentKey, o -> {
                    button.getTriggers().setTriggerMap(o);
                });
            }
        }
    }

    private static void parseTriggerListCommand(Element elementNode, String parentKey, Consumer<LinkedHashMap<String, Trigger>> consumer) throws JAXBException {
        LinkedHashMap<String, Trigger> linkedHashMap = new LinkedHashMap<>();
        NodeList nodeList = elementNode.getElementsByTagName("Trigger");
        if (nodeList != null && nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element triggerElement = (Element) nodeList.item(i);
                int finalI = i;
                parseCommand(triggerElement, parentKey + "_Trigger", o -> {
                    Trigger trigger = new Trigger();
                    trigger.setCommandMap(o);
                    if (Optional.ofNullable(triggerElement.getAttribute("action")).map(action -> (!"".equals(action))).orElse(false)) {
                        trigger.setAction(triggerElement.getAttribute("action"));
                    }
                    linkedHashMap.put(parentKey + "_Triggers_" + finalI, trigger);
                });
            }
            consumer.accept(linkedHashMap);
        }
    }

    private static void parseTriggerCommand(Element elementNode, String parentKey, Consumer<LinkedHashMap<String, BaseCommand>> consumer) throws JAXBException {
        NodeList nodeList = elementNode.getElementsByTagName("Trigger");
        if (nodeList != null && nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element triggerElement = (Element) nodeList.item(i);
                parseCommand(triggerElement, parentKey + "_Trigger", consumer);
            }
        }
    }

    private static void parseCommand(Element elementNode, String parentKey, Consumer<LinkedHashMap<String, BaseCommand>> consumer) throws JAXBException {
        LinkedHashMap<String, BaseCommand> linkedHashMap = new LinkedHashMap<>();
        NodeList nodeList = elementNode.getChildNodes();
        if (nodeList != null && nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                String action = Optional.ofNullable(elementNode.getAttribute("action")).map(key -> ("".equals(key)) ? "" : "_" + key).orElse("");
                String key = parentKey + action;
                switch (node.getNodeName()) {
                    case "VariableCommand":
                        key += "_VariableCommand_" + i;
                        VariableCommand variableCommand = (VariableCommand) XmlUtils.xml2java(VariableCommand.class, node);
                        variableCommand.setId(key);
                        linkedHashMap.put(key, variableCommand);
                        consumer.accept(linkedHashMap);
                        break;
                    case "Command":
                        key += "_Command_" + i;
                        Command command = (Command) XmlUtils.xml2java(Command.class, node);
                        command.setId(key);
                        linkedHashMap.put(key, command);
                        consumer.accept(linkedHashMap);
                        break;
                    case "SoundCommand":
                        key += "_SoundCommand_" + i;
                        SoundCommand soundCommand = (SoundCommand) XmlUtils.xml2java(SoundCommand.class, node);
                        soundCommand.setId(key);
                        linkedHashMap.put(key, soundCommand);
                        consumer.accept(linkedHashMap);
                        break;
                    case "VibratorCommand":
                        key += "_VibratorCommand_" + i;
                        VibratorCommand vibratorCommand = (VibratorCommand) XmlUtils.xml2java(VibratorCommand.class, node);
                        vibratorCommand.setId(key);
                        linkedHashMap.put(key, vibratorCommand);
                        consumer.accept(linkedHashMap);
                        break;
                    case "ExternCommand":
                        key += "_ExternCommand_" + i;
                        ExternCommand externCommand = (ExternCommand) XmlUtils.xml2java(ExternCommand.class, node);
                        externCommand.setId(key);
                        linkedHashMap.put(key, externCommand);
                        consumer.accept(linkedHashMap);
                        break;
                    case "IntentCommand":
                        key += "_IntentCommand_" + i;
                        IntentCommand intentCommand = (IntentCommand) XmlUtils.xml2java(IntentCommand.class, node);
                        intentCommand.setId(key);
                        linkedHashMap.put(key, intentCommand);
                        consumer.accept(linkedHashMap);
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public static void saveLockScreenXml(ThemeFileInfo themeFileInfo, Document document, Lockscreen lockscreen, File file) {
        if (themeFileInfo != null) {
            AutoSaveUtils.getInstance(themeFileInfo).saveRecord(lockscreen);
        }
        Element rootElement = document.getDocumentElement();
        rootElement.setTextContent("");
        transformViewMapNode(document, rootElement, lockscreen.getLockScreenViewMap());
        try {
            new ReadXmlUtils().saveXml(document, file);
        } catch (TransformerException e) {
            e.printStackTrace();
        }
    }

    private static void transformViewMapNode(Document document, Element rootElement, LinkedHashMap<String, BaseView> viewMap) {
        viewMap.keySet().forEach(name -> {
            BaseView baseView = viewMap.get(name);
            if (baseView instanceof Var) {
                transformNode(Var.class, rootElement, baseView);
            } else if (baseView instanceof Image) {
                if (Optional.ofNullable(baseView.getFitFX()).orElse(false)) {
                    if (baseView.getFitFX()) {
                        baseView.setY(Optional.ofNullable(baseView.getY()).map(y -> {
                            if (y.contains("#screen_height-1920+")) {
                                return y;
                            } else {
                                return "#screen_height-1920+" + y;
                            }
                        }).orElse("0"));
                    } else {
                        baseView.setY(Optional.ofNullable(baseView.getY())
                                .map(y -> y.replace("#screen_height-1920+", ""))
                                .orElse("0"));
                    }
                }
                transformNode(Image.class, rootElement, baseView);
            } else if (baseView instanceof Button) {
                transformNode(Button.class, rootElement, baseView);
                saveTriggersViewMapNode(document, rootElement, baseView);
            } else if (baseView instanceof Wallpaper) {
                transformNode(Wallpaper.class, rootElement, baseView);
            } else if (baseView instanceof Unlocker) {
                transformNode(Unlocker.class, rootElement, baseView);
                saveSliderViewMapNode(rootElement, baseView);
            } else if (baseView instanceof Slider) {
                transformNode(Slider.class, rootElement, baseView);
                saveSliderViewMapNode(rootElement, baseView);
            } else if (baseView instanceof ExternalCommands) {
                transformNode(ExternalCommands.class, rootElement, baseView);
            } else if (baseView instanceof Group) {
                Element group = document.createElement("Group");
                if (baseView.getName() != null) {
                    group.setAttribute("name", baseView.getName());
                }
                if (baseView.getX() != null) {
                    group.setAttribute("x", baseView.getX());
                }
                if (baseView.getY() != null) {
                    group.setAttribute("y", baseView.getY());
                }
                if (baseView.getW() != null) {
                    group.setAttribute("w", baseView.getW());
                }
                if (baseView.getH() != null) {
                    group.setAttribute("h", baseView.getH());
                }
                if (baseView.getAlpha() != null) {
                    group.setAttribute("alpha", baseView.getAlpha());
                }
                if (baseView.getVisibility() != null) {
                    group.setAttribute("visibility", baseView.getVisibility());
                }
                transformViewMapNode(document, group, ((Group) baseView).getGroupViewMap());
                rootElement.appendChild(group);
            } else if (baseView instanceof Comment) {
                rootElement.appendChild(document.createComment(((Comment) baseView).getValue()));
            } else {

            }
        });
    }

    private static void saveSliderViewMapNode(Element rootElement, BaseView baseView) {
        Slider slider = (Slider) baseView;
        Element sliderElement = (Element) rootElement.getLastChild();
        if (slider.getStartPoint().getReachedState() != null) {
            LinkedHashMap<String, BaseCommand> linkedHashMapCommand = Optional.ofNullable(slider.getStartPoint().getReachedState().getTrigger())
                    .map(map -> Optional.ofNullable(map.getCommandMap()).orElse(null))
                    .orElse(null);
            if (linkedHashMapCommand != null) {
                saveSliderViewMapNode(linkedHashMapCommand, sliderElement, "ReachedState");
            }
        }
        if (slider.getStartPoint().getReachedUpState() != null) {
            LinkedHashMap<String, BaseCommand> linkedHashMapCommand = Optional.ofNullable(slider.getStartPoint().getReachedUpState().getTrigger())
                    .map(map -> Optional.ofNullable(map.getCommandMap()).orElse(null))
                    .orElse(null);
            if (linkedHashMapCommand != null) {
                saveSliderViewMapNode(linkedHashMapCommand, sliderElement, "ReachedUpState");
            }
        }
        if (slider.getStartPoint().getReachedDownState() != null) {
            LinkedHashMap<String, BaseCommand> linkedHashMapCommand = Optional.ofNullable(slider.getStartPoint().getReachedDownState().getTrigger())
                    .map(map -> Optional.ofNullable(map.getCommandMap()).orElse(null))
                    .orElse(null);
            if (linkedHashMapCommand != null) {
                saveSliderViewMapNode(linkedHashMapCommand, sliderElement, "ReachedDownState");
            }
        }
        if (slider.getStartPoint().getNormalState() != null) {
            LinkedHashMap<String, BaseCommand> linkedHashMapCommand = Optional.ofNullable(slider.getStartPoint().getNormalState().getTrigger())
                    .map(map -> Optional.ofNullable(map.getCommandMap()).orElse(null))
                    .orElse(null);
            if (linkedHashMapCommand != null) {
                saveSliderViewMapNode(linkedHashMapCommand, sliderElement, "NormalState");
            }
        }
        if (slider.getStartPoint().getPressedState() != null) {
            LinkedHashMap<String, BaseCommand> linkedHashMapCommand = Optional.ofNullable(slider.getStartPoint().getPressedState().getTrigger())
                    .map(map -> Optional.ofNullable(map.getCommandMap()).orElse(null))
                    .orElse(null);
            if (linkedHashMapCommand != null) {
                saveSliderViewMapNode(linkedHashMapCommand, sliderElement, "PressedState");
            }
        }
    }

    private static void saveSliderViewMapNode(LinkedHashMap<String, BaseCommand> linkedHashMapCommand, Element sliderElement, String tagName) {
        Element stateElement = (Element) sliderElement.getElementsByTagName(tagName).item(0);
        Element triggerElement = (Element) stateElement.getElementsByTagName("Trigger").item(0);
        saveCommandViewMapNode(triggerElement, linkedHashMapCommand);
    }

    private static void saveTriggersViewMapNode(Document document, Element rootElement, BaseView baseView) {
        Button button = (Button) baseView;
        Element buttonElement = (Element) rootElement.getLastChild();
        Element triggers = (Element) buttonElement.getElementsByTagName("Triggers").item(0);
        LinkedHashMap<String, Trigger> linkedHashMapTrigger = button.getTriggers().getTriggerMap();
        linkedHashMapTrigger.keySet().forEach(key0 -> {
            LinkedHashMap<String, BaseCommand> linkedHashMapCommand = button.getTriggers().getTriggerMap().get(key0).getCommandMap();
            Element trigger = document.createElement("Trigger");
            if (Optional.ofNullable(linkedHashMapTrigger.get(key0).getAction()).map(action -> (!"".equals(action))).orElse(false)) {
                trigger.setAttribute("action", linkedHashMapTrigger.get(key0).getAction());
            }
            triggers.appendChild(saveCommandViewMapNode(trigger, linkedHashMapCommand));
        });
//        buttonElement.appendChild(triggers);
    }

    private static Element saveCommandViewMapNode(Element trigger, LinkedHashMap<String, BaseCommand> linkedHashMapCommand) {
        linkedHashMapCommand.keySet().forEach(key1 -> {
            BaseCommand baseCommand = linkedHashMapCommand.get(key1);
            if (baseCommand instanceof VariableCommand) {
                transformNode(VariableCommand.class, trigger, baseCommand);
            } else if (baseCommand instanceof Command) {
                transformNode(Command.class, trigger, baseCommand);
            } else if (baseCommand instanceof SoundCommand) {
                transformNode(SoundCommand.class, trigger, baseCommand);
            } else if (baseCommand instanceof VibratorCommand) {
                transformNode(VibratorCommand.class, trigger, baseCommand);
            } else if (baseCommand instanceof ExternCommand) {
                transformNode(ExternCommand.class, trigger, baseCommand);
            } else if (baseCommand instanceof IntentCommand) {
                transformNode(IntentCommand.class, trigger, baseCommand);
            }
        });
        return trigger;
    }

    private static void transformNode(Class<?> className, Element element, Object object) {
        try {
            JAXBContext context = JAXBContext.newInstance(className);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
            marshaller.marshal(object, element);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}
